﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System.IO;
using Box2D.XNA;



    /// <summary>
    /// game class that has everything
    /// </summary>
    //12/22 add an animations dictionary to sprite class which holds each anim (frame/duration),
    //write code to play these animations.
    //5/20 work on input in sprite class
    //5/19 look up "working@" to find place
    //just finished fixing setup file for main menu
    //4/16 working on running anim. should work on jumping anim, gravity, collision with floor, collision with walls
    //3/18/14 look through and comment all the code. clean up where appropriate. and make consistent the pixels
    //match key states - done (3/27/14)

    /// <summary>
    /// the game class is a giant singleton class that holds all the global variables and functions of the game.
    /// Everything in the game (menu, the actual game, settings, etc) is accessed through here
    /// </summary>
    /// 

    public enum ButtonAction {Right, Left, Boost, Attack, Jump, Roll, HardRight, HardLeft};
    
    public class Game : Microsoft.Xna.Framework.Game
    {
        private Menu menu;
        GraphicsDeviceManager graphics;
        public enum State {Menu, InGame};
        State state;
        Texture2D stageBg;
        Sprite[] players;
        Sprite enemy;
        // Button[] buttons;
        World _physicsWorld;
        Body boxBody;

        float boxX = 500;
        float boxY = 300;
        float boxW = 50;
        float boxH = 50;
        Texture2D mHealthBar;
        List<string []> stageMatrix = new List<string[]>();
        private Texture2D stageTile;

        int screenWidth;
        int screenHeight;

        Box2D.XNA.TestBed.Framework.DebugDraw render = new Box2D.XNA.TestBed.Framework.DebugDraw();

        public World getWorld() { return _physicsWorld; }
        public Game()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            state=State.Menu;
            graphics.PreferredBackBufferWidth = 1280;
            graphics.PreferredBackBufferHeight = 720;
            graphics.IsFullScreen = false; 
        }
        /// <summary>
        /// initializes the program, including reading setup files and puts you on the main menu
        /// </summary>
        protected override void Initialize()
        {
            //sets the phyiscs for the game. perhaps should be moved else where?
            float gravity = 100;
            _physicsWorld = new World(new Vector2(0, gravity), true);
            /*
            //walls to bind level, todo: be made when loading stage
            BodyDef rightWallBodyDef = new BodyDef();
            rightWallBodyDef.position = new Vector2(-10, 300);
            Body rightWallBody = _physicsWorld.CreateBody(rightWallBodyDef);
            PolygonShape rightWallBox = new PolygonShape();
            rightWallBox.SetAsBox(10, 390);
            rightWallBody.CreateFixture(rightWallBox, 0);

            BodyDef leftWallBodyDef = new BodyDef();
            leftWallBodyDef.position = new Vector2(1200, 300);
            Body leftWallBody = _physicsWorld.CreateBody(leftWallBodyDef);
            PolygonShape leftWallBox = new PolygonShape();
            leftWallBox.SetAsBox(10, 390);
            leftWallBody.CreateFixture(leftWallBox, 0);
            */
            //initialize menu
            String setupFilePath = new DirectoryInfo(Directory.GetCurrentDirectory()).Parent.Parent.Parent.Parent.FullName 
                + "\\FightPGContent\\setup.txt";

            /*
             * first line of set file tells you how many buttons
             * next the buttons info is put in each line (texture, texture highlighted, off set from middle x, offset y)
             * menu background texture
             * should replace all setup files with something more readable like xml or property file
             */
            string[] setupFile = System.IO.File.ReadAllLines(setupFilePath);

            int buttonCount = Convert.ToInt32(setupFile[0]);//number of buttons there are
            Button[] buttons = new Button[buttonCount];
            int attributeCount = 4; //number of attributes to describe buttons
            
            for (int x = 0; x < buttonCount; x++)
            {
                buttons[x] = new Button(graphics.GraphicsDevice.Viewport.Width / 2 + Convert.ToInt32(setupFile[x * attributeCount + 3]),
                    graphics.GraphicsDevice.Viewport.Height / 2 + Convert.ToInt32(setupFile[x * attributeCount + 4]),
                    Content.Load<Texture2D>(setupFile[x * attributeCount + 1]),
                    Content.Load<Texture2D>(setupFile[x * attributeCount + 2]));
            }
            int menuBGLine= attributeCount * buttonCount+1;
            Texture2D menuBackground=Content.Load<Texture2D>(setupFile[menuBGLine]);
            menu=new Menu(menuBackground, buttons, this);


            screenWidth = graphics.GraphicsDevice.PresentationParameters.BackBufferWidth;
            screenHeight = graphics.GraphicsDevice.PresentationParameters.BackBufferHeight; 

            base.Initialize();
        }
        protected override void Update(GameTime gameTime)
        {
            
            //select menu buttons
            KeyboardState keyState = Keyboard.GetState();
            GamePadState gamePadState = GamePad.GetState(PlayerIndex.One);
            List<ButtonAction> keyButtons = new List<ButtonAction>();
            List<ButtonAction> gamePadButtons = new List<ButtonAction>();
            List<ButtonAction> playerButtons = new List<ButtonAction>();

            if (keyState.IsKeyDown(Keys.D))
                keyButtons.Add(ButtonAction.Right);
            if (keyState.IsKeyDown(Keys.A))
                keyButtons.Add(ButtonAction.Left);
            if (keyState.IsKeyDown(Keys.LeftShift) || keyState.IsKeyDown(Keys.RightShift))
                keyButtons.Add(ButtonAction.Boost);
            if (keyState.IsKeyDown(Keys.J))
                keyButtons.Add(ButtonAction.Attack);
            if (keyState.IsKeyDown(Keys.Space))
                keyButtons.Add(ButtonAction.Jump);
            if (keyState.IsKeyDown(Keys.K))
                keyButtons.Add(ButtonAction.Roll);

            playerButtons = keyButtons;
            if (gamePadState.IsConnected)
            {
                if (gamePadState.ThumbSticks.Left.X > -1.0f && gamePadState.ThumbSticks.Left.X < 0)
                    gamePadButtons.Add(ButtonAction.Left);
                if (gamePadState.ThumbSticks.Left.X == -1.0f)
                {
                    gamePadButtons.Add(ButtonAction.HardLeft);
                    gamePadButtons.Add(ButtonAction.Left);
                }
                if (gamePadState.ThumbSticks.Left.X < 1.0f && gamePadState.ThumbSticks.Left.X > 0)
                    gamePadButtons.Add(ButtonAction.Right);
                if (gamePadState.ThumbSticks.Left.X == 1.0f)
                {
                    gamePadButtons.Add(ButtonAction.HardRight);
                    gamePadButtons.Add(ButtonAction.Right);
                }
                if (gamePadState.Buttons.Y == ButtonState.Pressed)
                    gamePadButtons.Add(ButtonAction.Jump);

                playerButtons = gamePadButtons;
            }

            if (state == State.Menu)
            {
                state = menu.updateSelected(keyState);
                if (state == State.InGame)
                {
                    loadGame();
                }
            }
            else if (state == State.InGame)
            {
                for (int x = 0; x < players.Length; x++)
                {
                    players[x].update(gameTime, keyButtons);
                }
                _physicsWorld.Step((float)gameTime.ElapsedGameTime.TotalSeconds, 10, 3);
            }
            Draw();//maybe i should have seperate draws for drawing seperate shit (i.e. drawMenu, drawGame)
        }
        
        static void Main(string[] args)
        {
            using (Game game = new Game())
            {
                game.Run();
            }
        }

        public void Draw()
        {
            if (state == State.Menu)
            {
                Button[] buttons = menu.getButtons();
                this.GraphicsDevice.Clear(Color.CornflowerBlue);
                menu.spriteBatch.Begin();
                Rectangle screenRectangle = new Rectangle(0, 0, screenWidth, screenHeight);
                menu.spriteBatch.Draw(menu.getBackground(), screenRectangle, Color.White);
                for (int x = 0; x < buttons.Length; x++)
                {
                    Vector2 buttonPos = new Vector2(buttons[x].getX(), buttons[x].getY());
                    Texture2D buttonText = buttons[x].getTexture();
                    if (menu.getSelected() == x)
                    {
                        buttonText = buttons[x].getTextureHighlight();
                    }
                    menu.spriteBatch.Draw(buttonText, buttonPos, Color.White);
                }
                menu.spriteBatch.End();
            }
            else if (state == State.InGame)
            {
                this.GraphicsDevice.Clear(Color.CornflowerBlue);//background color
                menu.spriteBatch.Begin();
                //draw  stage

                int length = 20;
                int width = 50;
                int y = 0;
                foreach (String[] line in stageMatrix) 
                {
                    int x = 0;
                    foreach (String tile in line)
                    {
                        String [] tileInfo = tile.Split(';');
                        if (tileInfo[0].Length > 0  && Convert.ToInt32(tileInfo[0]) > 0)
                        {
                            Rectangle rect = new Rectangle(x*width, y*length, width, length);
                            menu.spriteBatch.Draw(stageTile, rect, Color.White);
                            
                        }
                        x++;
                    }
                    y++;
                }
                
                //draw position of character in game
                string output = players[0].getSpriteBody().GetWorldPoint(new Vector2(0)).ToString();
                SpriteFont CourierNew = Content.Load<SpriteFont>("SpriteFont1");
                // Find the center of the string
                Vector2 FontOrigin = CourierNew.MeasureString(output) / 2;
                // Draw the string
                Vector2 FontPos = new Vector2(100, 500);
                menu.spriteBatch.DrawString(CourierNew, output, FontPos, Color.LightGreen,
                    0, FontOrigin, 1.0f, SpriteEffects.None, 0.5f);

                output = players[0].getSpriteBody().IsAwake().ToString();
                FontPos = new Vector2(500, 500);
                menu.spriteBatch.DrawString(CourierNew, output, FontPos, Color.LightGreen,
                    0, FontOrigin, 1.0f, SpriteEffects.None, 0.5f);

                //draw health bars
                float healthBarWidth = 100;
                float healthBarLength = 15;
                
                //additional numbers to exclude borders
                int rectY = players[0].getAnimationFrame() / players[0].getPanelDimensionX() * players[0].getPanelLength() + 2;
                int rectX = players[0].getAnimationFrame() % players[0].getPanelDimensionX() * players[0].getPanelWidth() + 2;

                Rectangle sourceRect = new Rectangle(rectX, rectY, players[0].getPanelWidth() - 4, players[0].getPanelLength() - 4);
                //menu.spriteBatch.Draw(players[0].getSpriteSheet(), new Vector2((float)players[0].getX(), (float)players[0].getY()), sourceRect, Color.White);
                Vector2 point = new Vector2((float)players[0].getSpriteBody().GetWorldPoint(new Vector2(0)).X - players[0].getPanelWidth()/2,
                        (float)players[0].getSpriteBody().GetWorldPoint(new Vector2(0)).Y - players[0].getPanelLength()/2);
                menu.spriteBatch.Draw(players[0].getSpriteSheet(), point, sourceRect, Color.White);

                healthBarWidth = players[0].getHealth();

                point.X = point.X + players[0].getPanelWidth() / 2 - healthBarWidth / 2;
                point.Y = point.Y - healthBarLength - 10;

                menu.spriteBatch.Draw(mHealthBar, point, new Rectangle(0, 45, (int)healthBarWidth, (int)healthBarLength), Color.Blue);

                rectY = enemy.getAnimationFrame() / enemy.getPanelDimensionX() * enemy.getPanelLength() + 1;
                rectX = enemy.getAnimationFrame() % enemy.getPanelDimensionX() * enemy.getPanelWidth() + 1;

                if (enemy.getHealth()>0)
                {
                    sourceRect = new Rectangle(rectX, rectY, enemy.getPanelWidth() - 2, enemy.getPanelLength() - 2);
                    //menu.spriteBatch.Draw(players[0].getSpriteSheet(), new Vector2((float)players[0].getX(), (float)players[0].getY()), sourceRect, Color.White);
                    point = new Vector2((float)enemy.getSpriteBody().GetWorldPoint(new Vector2(0)).X - enemy.getPanelWidth() / 2,
                            (float)enemy.getSpriteBody().GetWorldPoint(new Vector2(0)).Y - enemy.getPanelLength() / 2);

                    healthBarWidth = enemy.getHealth();
                    
                    menu.spriteBatch.Draw(enemy.getSpriteSheet(), point, sourceRect, Color.White);
                    point.X = point.X + enemy.getPanelWidth()/2 - healthBarWidth/2;
                    point.Y = point.Y - healthBarLength - 10;

                    menu.spriteBatch.Draw(mHealthBar, point, new Rectangle(0, 45, (int)healthBarWidth, (int)healthBarLength), Color.Red);
                    //menu.spriteBatch.Draw(Content.Load<Texture2D>("black_dot"), new Vector2(0, 0), Color.AliceBlue);
                }

                //don't know what this did
                /*Body bod = _physicsWorld.GetBodyList();
                while (bod.GetNext() != null)
                {
                    
                    bod = bod.GetNext();
                    Transform xf;
                    bod.GetTransform(out xf);
                    Fixture fixture = bod.GetFixtureList();
                    PolygonShape poly = (PolygonShape)fixture.GetShape();
                    int vertexCount = poly._vertexCount;
                    FixedArray8<Vector2> vertices = new FixedArray8<Vector2>();

                    for (int i = 0; i < vertexCount; ++i)
                    {
                        vertices[i] = MathUtils.Multiply(ref xf, poly._vertices[i]);
                    }

                    render.DrawSolidPolygon(ref vertices, vertexCount, Color.Crimson);

                }*/
                menu.spriteBatch.End();
            }
        }
        public void stageSelect()
        {
            Texture2D stageSelect = Content.Load<Texture2D>("stageSelect");
        }
        public void loadGame()//change when you add a selection screen
        {
            mHealthBar = Content.Load<Texture2D>("HealthBar2");
            stageBg = Content.Load<Texture2D>("stageBG");
            stageTile = Content.Load<Texture2D>("stageTile");
            players = new Sprite[1];
            players[0] = new Mobby(new Vector2(100, 580), new String[] {"mobbySpriteSheet", "mobbyAttackSpriteSheet"}, this, 0x0002);
            enemy = new Mobby(new Vector2(1000, 580), new String[] { "mobbySpriteSheet", "mobbyAttackSpriteSheet" }, this, 0x0004);
            //graphics.GraphicsDevice.Viewport.Width / 2) + 100, graphics.GraphicsDevice.Viewport.Height/2

            String setupFilePath = new DirectoryInfo(Directory.GetCurrentDirectory()).Parent.Parent.Parent.Parent.FullName
                + "\\FightPGContent\\stage1.csv";

            string[] setupFile = System.IO.File.ReadAllLines(setupFilePath);
            foreach (String line in setupFile)
            {
                stageMatrix.Add(line.Split(','));
            }
            //add stage tiles to physics
            int length = 20;
            int width = 50;
            int y = 0;
            foreach (String[] line in stageMatrix)
            {
                int x = 0;
                foreach (String tile in line)
                {
                    String[] tileInfo = tile.Split(';');
                    if (tileInfo[0].Length > 0 && Convert.ToInt32(tileInfo[0]) > 0)
                    {
                        BodyDef tileBD = new BodyDef();
                        float test1 = x * width + width / 2;
                        float test2 = y * length + length / 2;
                        tileBD.position = new Vector2(test1, test2);
                        Body tileBody = _physicsWorld.CreateBody(tileBD);
                        PolygonShape tileBox = new PolygonShape();
                        tileBox.SetAsBox(width, length);
                        tileBody.CreateFixture(tileBox, 0);
                    }
                    x++;
                }
                y++;
            }
        }
    }


